package pda2glc;

import java.util.ArrayList;

/**
 *
 * @author Steiger
 */
public class Automato {

    private ArrayList<String> alfabetoLinguagem = new ArrayList<String>();
    private ArrayList<String> alfabetoPilha = new ArrayList<String>();
    private ArrayList<String> estados = new ArrayList<String>();
    private ArrayList<String> temp = new ArrayList<String>();
    private ArrayList<ArrayList<String>> matrizEstados = new ArrayList<ArrayList<String>>();
        
    public Automato() { }

    public void addEstado( String estado )
    {
        estados.add( estado );
    }

    public void addAlfabetoLinguagem( String alfabeto )
    {
        alfabetoLinguagem.add( alfabeto );
    }

    public void addAlfabetoPilha( String alfabeto )
    {
        alfabetoPilha.add( alfabeto );
    }

    /**
     * Gera as producoes a partir do simbolo inicial S.
     * Segundo Hopcroft:
     * 'Para cada estado q, a gramatica terá a produção S - [q0 Z p]. Um simbolo
     * como [q0 Z p] tem a intencao de gerar todas as strings w, que fazem o
     * automato P remover Z da pilha enquanto indo do estado q0 até p. Sendo
     * assim, essas producoes afirmam que S gerará todas as Strings que podem
     * ser lidas por P fazendo-o esvaziar a pilha.
     */
    public void gerarProducoesIniciais()
    {
        for ( String q : estados )
            System.out.println( "S - [" + estados.get( 0 ) + " Z " + q + "]" );
    }

    /**
     * Funcao recursiva para criar os arranjos de todos os estados.
     * Para ganho de performance, essa funcao soh deve ser usada no caso de
     * numSimbolos > 1!
     */
    private void criarArranjos( int numSimbolos, int contador )
    {
        for ( String q : estados )
        {       
            temp.set( contador, q ); // Adiciona cada estado a temp.
            
            if ( contador < numSimbolos - 1 )
            {
                criarArranjos( numSimbolos, contador + 1 );
            } 
            else
            {
                // Cria uma nova ArrayList
                ArrayList<String> tempClone = new ArrayList<String>();
                
                // Para cada p em temp, adicionar em tempClone
                for ( String p : temp ) tempClone.add( p );
                
                matrizEstados.add( tempClone );
            }
        }
    }

    /**
     * Seja (q a X) = (r Y1Y2..Yk), onde:
     * 'a' é um simbolo do alfabeto (ou lambda)
     * 'k' pode ser qualquer numero, incluindo zero (r, lambda)
     * Entao para todas as listas de estado r1, r2, ..., rk, G tera a producao:
     * [q X rk] - a[r Y1 r1][r1 Y2 r2]...[rk-1 Yk rk]
     * Nos casos onde o PDA empilha 'y' ou somente um simbolo, a producao eh
     * simples.
     * Para dois ou mais simbolos, eh necessario criar uma matriz com a
     * combinacao de todos os estados.
     */
    public void gerarGramatica( String estadoPartida, 
                                String simbolo, 
                                String pop, 
                                String estadoChegada, 
                                String push )
    { 
        // Se empilha Lambda (nada)
        if ( push.equals( "y" ) )
        {
            System.out.println( "[" + estadoPartida + " " + pop + " " +
                    estadoChegada + "] - " + simbolo );
        } 
        
        // Se empilha um unico simbolo, diferente de lambda
        else if ( push.length() == 1 && !push.equals( "y" ) )
        {
            for ( String q : estados )
            {
                System.out.println( "[" + estadoPartida + " " + pop + " " +
                        q + "] - " + simbolo + "[" + estadoChegada + " " + 
                        push + " " + q + "]" );
            }
        } 
        
        // Se empilha varios simbolos, eh necessario criar uma matriz. 
        else
        {
            // Limpa os atributos, caso estejam 'sujos'.
            temp.clear();
            for ( ArrayList<String> array : matrizEstados ) array.clear();
            matrizEstados.clear();
            
            for ( String q : estados ) temp.add( q );
            
            criarArranjos( push.toCharArray().length, 0 );
                
            for ( ArrayList ParEstados : matrizEstados )
            {
                // Imprime o inicio da variavel (ex: "[q0 Z q0] - [q0")
                System.out.print( "[" + estadoPartida + " " + pop + " " +
                        ParEstados.get( ParEstados.size() - 1 ) + "] - " + 
                        simbolo + "[" + estadoChegada);
                
                for ( int f = 0; f < push.length(); f++ )
                {
                    // Imprime o resto da variavel (ex: "Z q1]")
                    System.out.print(" " + push.substring( f, f + 1 ) + " " + 
                            ParEstados.get( f ) + "]");
                    
                    // Se nao for o ultimo simbolo, imprime o inicio de outra variavel (ex: "[q1")
                    if ( f != push.length() - 1 ) 
                    {
                        System.out.print( "[" + ParEstados.get( f ) );
                    }
                    // Se for, inicia uma nova linha.
                    else
                    {
                        System.out.println();
                    }
                }
            } // end for
        } // end else
    }
}
